package io.gitee.zhangbinhub.acp.core.common.security

import io.gitee.zhangbinhub.acp.core.common.base.BaseEncrypt
import java.security.Key
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec

/**
 * @author zhang by 10/07/2019
 * @since JDK 11
 */
object DesEncrypt : BaseEncrypt() {

    private val ZERO_IVC = byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0)

    private const val CRYPT_TYPE_3DES_CBC = "DESede/CBC/PKCS5Padding"

    private const val CRYPT_TYPE_3DES_ECB = "DESede/ECB/PKCS5Padding"

    private const val CRYPT_TYPE_DES_CBC = "DES/CBC/PKCS5Padding"

    private const val CRYPT_TYPE_DES_ECB = "DES/ECB/PKCS5Padding"

    /**
     * 3DES加密cbc模式 DESede/CBC/PKCS5Padding
     *
     * @param plainText 待加密数据
     * @param key       秘钥
     * @return 加密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun encryptBy3DesCbc(
        plainText: String, key: Key,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        iv: ByteArray = ZERO_IVC
    ): String = encrypt(plainText, key, CRYPT_TYPE_3DES_CBC, inputEncode, outputEncode, iv)

    /**
     * 3DES解密cbc模式 DESede/CBC/PKCS5Padding
     *
     * @param encryptedText 待解密数据
     * @param key           秘钥
     * @return 解密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun decryptBy3DesCbc(
        encryptedText: String, key: Key,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        iv: ByteArray = ZERO_IVC
    ): String = decrypt(encryptedText, key, CRYPT_TYPE_3DES_CBC, inputEncode, outputEncode, iv)

    /**
     * 3DES加密ecb模式 DESede/ECB/PKCS5Padding
     *
     * @param plainText 待加密数据
     * @param key       秘钥
     * @return 加密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun encryptBy3DesEcb(
        plainText: String, key: Key,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        iv: ByteArray = ZERO_IVC
    ): String = encrypt(plainText, key, CRYPT_TYPE_3DES_ECB, inputEncode, outputEncode, iv)

    /**
     * 3DES解密ecb模式 DESede/ECB/PKCS5Padding
     *
     * @param encryptedText 待解密数据
     * @param key           秘钥
     * @return 解密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun decryptBy3DesEcb(
        encryptedText: String, key: Key,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        iv: ByteArray = ZERO_IVC
    ): String = decrypt(encryptedText, key, CRYPT_TYPE_3DES_ECB, inputEncode, outputEncode, iv)

    /**
     * DES加密cbc模式 DES/CBC/PKCS5Padding
     *
     * @param plainText 待加密数据
     * @param key       秘钥
     * @return 加密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun encryptByDesCbc(
        plainText: String, key: Key,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        iv: ByteArray = ZERO_IVC
    ): String = encrypt(plainText, key, CRYPT_TYPE_DES_CBC, inputEncode, outputEncode, iv)

    /**
     * DES解密cbc模式 DES/CBC/PKCS5Padding
     *
     * @param encryptedText 待解密数据
     * @param key           秘钥
     * @return 解密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun decryptByDesCbc(
        encryptedText: String, key: Key,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        iv: ByteArray = ZERO_IVC
    ): String = decrypt(encryptedText, key, CRYPT_TYPE_DES_CBC, inputEncode, outputEncode, iv)

    /**
     * DES加密ecb模式 DES/ECB/PKCS5Padding
     *
     * @param plainText 待加密数据
     * @param key       秘钥
     * @return 加密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun encryptByDesEcb(
        plainText: String, key: Key,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        iv: ByteArray = ZERO_IVC
    ): String = encrypt(plainText, key, CRYPT_TYPE_DES_ECB, inputEncode, outputEncode, iv)

    /**
     * DES解密ecb模式 DES/ECB/PKCS5Padding
     *
     * @param encryptedText 待解密数据
     * @param key           秘钥
     * @return 解密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun decryptByDesEcb(
        encryptedText: String, key: Key,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        iv: ByteArray = ZERO_IVC
    ): String = decrypt(encryptedText, key, CRYPT_TYPE_DES_ECB, inputEncode, outputEncode, iv)

    /**
     * 加密
     *
     * @param plainText 待加密数据
     * @param key       秘钥
     * @param cryptType 加密类型，默认 DESede/ECB/PKCS5Padding
     * @return 加密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    fun encrypt(
        plainText: String,
        key: Key,
        cryptType: String = CRYPT_TYPE_3DES_ECB,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        iv: ByteArray = ZERO_IVC
    ): String {
        val cipher = Cipher.getInstance(cryptType)
        if (cryptType.uppercase().contains("ECB")) {
            cipher.init(Cipher.ENCRYPT_MODE, key)
        } else {
            cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))
        }
        return encodeToString(cipher.doFinal(decodeToBytes(plainText, inputEncode)), outputEncode)
    }

    /**
     * 解密
     *
     * @param encryptedText 待解密数据
     * @param key           秘钥
     * @param cryptType     加密类型，默认 DESede/ECB/PKCS5Padding
     * @return 解密结果
     */
    @JvmStatic
    @Throws(Exception::class)
    fun decrypt(
        encryptedText: String,
        key: Key,
        cryptType: String = CRYPT_TYPE_3DES_ECB,
        inputEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64,
        outputEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE,
        iv: ByteArray = ZERO_IVC
    ): String {
        val cipher = Cipher.getInstance(cryptType)
        if (cryptType.uppercase().contains("ECB")) {
            cipher.init(Cipher.DECRYPT_MODE, key)
        } else {
            cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv))
        }
        return encodeToString(cipher.doFinal(decodeToBytes(encryptedText, inputEncode)), outputEncode)
    }

}
